package cn.wolfcode.web.controller;

import cn.wolfcode.common.domain.UserInfo;
import cn.wolfcode.common.exception.BusinessException;
import cn.wolfcode.common.util.AssertUtils;
import cn.wolfcode.common.web.Result;
import cn.wolfcode.common.web.anno.RequireLogin;
import cn.wolfcode.common.web.resolver.RequestUser;
import cn.wolfcode.domain.OrderInfo;
import cn.wolfcode.domain.SeckillProductVo;
import cn.wolfcode.mq.DefaultSendCallback;
import cn.wolfcode.mq.MQConstant;
import cn.wolfcode.mq.OrderMessage;
import cn.wolfcode.redis.CommonRedisKey;
import cn.wolfcode.redis.SeckillRedisKey;
import cn.wolfcode.service.IOrderInfoService;
import cn.wolfcode.service.ISeckillProductService;
import cn.wolfcode.web.msg.SeckillCodeMsg;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.Calendar;
import java.util.Date;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;


@RestController
@RequestMapping("/order")
@Slf4j
public class OrderInfoController {

    /**
     * 用于标记已经售完的商品
     */
    private static final Map<Long, Boolean> STOCK_OVER_FLOW_MAP = new ConcurrentHashMap<>();

    @Autowired
    private ISeckillProductService seckillProductService;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Autowired
    private RocketMQTemplate rocketMQTemplate;

    @Autowired
    private IOrderInfoService orderInfoService;

    public static void deleteKey(Long key) {
        STOCK_OVER_FLOW_MAP.remove(key);
    }

    /**
     * 优化前：
     * 测试数据：500 个用户，100 线程，执行 50 次
     * 测试情况：330 QPS
     */
    @RequireLogin
    @RequestMapping("/doSeckill")
    public Result<?> doSeckill(Integer time, Long seckillId, @RequestUser UserInfo userInfo, @RequestHeader("token") String token) {

        // 1. 基于 token 获取到用户信息(必须登录)
//        UserInfo userInfo = this.getUserByToken(token);

        // 判断是否已经卖完
        Boolean flag = STOCK_OVER_FLOW_MAP.get(seckillId);
        if (ObjectUtils.isNotEmpty(flag) && flag) {
            return Result.error(SeckillCodeMsg.SECKILL_STOCK_OVER);
        }
        // 2. 基于场次+秒杀id获取到秒杀商品对象
        SeckillProductVo sp = seckillProductService.selectByIdAndTime(seckillId, time);
        if (sp == null) {
            return Result.error(SeckillCodeMsg.REMOTE_DATA_ERROR);
        }

        // 3. 判断时间是否大于开始时间 && 小于 开始时间+2小时
        boolean range = this.betweenSeckillTime(sp);
        if (!range) {
            return Result.error(SeckillCodeMsg.OUT_OF_SECKILL_TIME_ERROR);
        }

        // 5. 判断用户是否重复下单
        // 基于用户 + 秒杀 id + 场次查询订单, 如果存在订单, 说明用户已经下过单
        String userOrderKey = SeckillRedisKey.SECKILL_STOCK_COUNT_HASH.join(seckillId + "");
//        Boolean absent = stringRedisTemplate.opsForHash().putIfAbsent(userOrderKey, userInfo.getPhone() + "", "1");
//        AssertUtils.isTrue(absent, "不能重复下单");
        Long orderCount = stringRedisTemplate.opsForHash().increment(userOrderKey, userInfo.getPhone() + "", 1);
        if (orderCount > 1) {
            return Result.error(SeckillCodeMsg.REPEAT_SECKILL);
        }
        // 4. 判断库存是否充足
        try {
            String hashKey = SeckillRedisKey.SECKILL_STOCK_COUNT_HASH.join(time + "");
            Long remain = stringRedisTemplate.opsForHash().increment(hashKey, seckillId, -1);
            AssertUtils.isTrue(remain >= 0, "商品已售罄");

            // 6. 执行下单操作(减少库存, 创建订单) 发送mq异步消息 创建订单
            rocketMQTemplate.asyncSend(MQConstant.ORDER_PENDING_TOPIC,
                    new OrderMessage(time, seckillId, token, userInfo.getPhone()),
                    new DefaultSendCallback("创建订单"));
            return Result.success("订单创建中，请稍后...");
        } catch (BusinessException e) {
            // 当库存不够时 标记当前库存已经卖完
            STOCK_OVER_FLOW_MAP.put(seckillId, true);

            // 将用户重复下单标识删除
            stringRedisTemplate.opsForHash().delete(userOrderKey, userInfo.getPhone() + "");
            return Result.error(e.getCodeMsg());
        } catch (Exception e) {
            e.printStackTrace();
        }
        return Result.defaultError();
    }


    /**
     * 判断时间是否大于开始时间 && 小于 开始时间+2小时
     */
    private boolean betweenSeckillTime(SeckillProductVo sp) {
        Calendar instance = Calendar.getInstance();
        instance.setTime(sp.getStartDate());
        // 设置小时
        instance.set(Calendar.HOUR_OF_DAY, sp.getTime());
        // 开始时间
        Date startDate = instance.getTime();
        // 结束时间 = 开始时间 + 2h
        instance.add(Calendar.HOUR_OF_DAY, 2);
        Date endTime = instance.getTime();
        // 当前时间
        long now = System.currentTimeMillis();
        return startDate.getTime() <= now && now < endTime.getTime();
    }

    @RequireLogin
    @GetMapping("/find")
    public Result<OrderInfo> find(String orderNo, @RequestUser UserInfo userInfo) {
        OrderInfo orderInfo = orderInfoService.selectByOrderNo(orderNo);
        Long userId = orderInfo.getUserId();
        if (orderInfo != null && !userInfo.getPhone().equals(userId)) {
            return Result.error(SeckillCodeMsg.ILLEGAL_OPERATION);
        }
        return Result.success(orderInfo);
    }

}
